package com.party.core.service.cos;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.party.common.utils.StringUtils;
import com.party.common.utils.img.CompressInput;
import com.party.common.utils.img.CompressResult;
import com.party.common.utils.img.ImgCompressUtil;
import com.party.common.utils.img.ImgUtil;
import com.party.core.model.system.SysConfig;
import com.party.core.service.system.ISysConfigService;
import com.party.file.cos.COSClient;
import com.party.file.cos.ClientConfig;
import com.party.file.cos.common_utils.CommonFileUtils;
import com.party.file.cos.dto.SearchResult;
import com.party.file.cos.dto.UploadResult;
import com.party.file.cos.exception.AbstractCosException;
import com.party.file.cos.meta.InsertOnly;
import com.party.file.cos.request.*;
import com.party.file.cos.sign.Credentials;
import com.party.file.cos.sign.Sign;
import com.party.file.cos_sts.CosStsClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;


/**
 * 对象存储
 * Created by Juliana
 *
 * @date 2018/1/16 0030
 * @time 15:25
 */
@Service
public class CosBizService {

    @Autowired
    ISysConfigService sysConfigService;

    @Value("#{cos['cos.appId']}")
    private Long APP_ID;

    @Value("#{cos['cos.secretId']}")
    private String SECRET_ID;

    @Value("#{cos['cos.secretKey']}")
    private String SECRET_KEY;

    @Value("#{cos['cos.bucket']}")
    private String BUCKET;

    @Value("#{cos['cos.bucket.file']}")
    private String BUCKET_FILE;

    @Value("#{cos['cos.region']}")
    private String REGION;

    @Value("#{cos['cos.region.full']}")
    private String REGION_FULL;

    private static Integer EXPIRED = 7200;

    private static String CONFIG_CODE = "cosSign";

    private static String CONFIG_CODE_FILE = "cosSignFile";

    protected static String QCLOUD_DOMAIN = "image.myqcloud.com";

    protected static Integer SUCCESS_CODE = 0;

    protected static String SUCCESS_MSG = "SUCCESS";

    // 0: 图片, 1: 文件
    private final Integer BUCKET_TYPE_IMG = 0;
    private final Integer BUCKET_TYPE_FILE = 1;

    private static COSClient cosClient = null;
    Logger logger = LoggerFactory.getLogger(getClass());

    @PostConstruct
    public void initCos() {
        // 初始化客户端配置
        ClientConfig clientConfig = new ClientConfig();
        // 设置bucket所在的区域，比如广州(gz), 天津(tj)
        clientConfig.setRegion(REGION);
        // 初始化秘钥信息
        Credentials cred = new Credentials(APP_ID, SECRET_ID, SECRET_KEY);
        // 初始化cosClient
        if (cosClient == null) {
            cosClient = new COSClient(clientConfig, cred);
        }
    }

    /**
     * 获取签名
     *
     * @param type  获取的签名类型
     * @param type: bucket类型 0: 图片, 1: 文件
     * @return 签名map
     */
    public Map<String, Object> getCosSign(String type, Integer bucketType) {
        Map<String, Object> retMap = new HashMap<String, Object>();
        String sign = "";
        String url = "";
        boolean flag = true;

        String bucket = "";
        String configCode = "";
        if (Integer.valueOf(0).equals(bucketType)) {
            bucket = BUCKET;
            configCode = CONFIG_CODE;
        } else if (Integer.valueOf(1).equals(bucketType)) {
            bucket = BUCKET_FILE;
            configCode = CONFIG_CODE_FILE;
        }

        try {
            if (type != null || !"".equals(type)) {
                if ("more".equals(type)) {
                    // 换成数据库读取
                    SysConfig cosSign = sysConfigService.findByCode(configCode);
                    if (null == cosSign) {
                        cosSign = refreshCosSign(bucketType);
                    }
                    sign = cosSign.getValue();
                } else if ("once".equals(type)) {
                    // 初始化秘钥信息
                    Credentials cred = new Credentials(APP_ID, SECRET_ID, SECRET_KEY);
                    sign = Sign.getOneEffectiveSign(bucket, "", cred);
                }
                flag = true;
            }
        } catch (Exception e) {
            flag = false;
            e.printStackTrace();
        }

        retMap.put("success", flag);
        retMap.put("ver", "V4");
        retMap.put("sign", sign);
        return retMap;
    }

    /**
     * 提供给定时任务刷新多次签名（ 一段时间内有效, 针对上传文件，重命名文件, 创建目录, 获取文件目录属性, 拉取目录列表）
     * 多次签名不需要每次生成，存数据库每隔一小时刷新一次
     *
     * @param type: bucket类型 0: 图片, 1: 文件
     * @return 签名
     */
    public SysConfig refreshCosSign(Integer type) {
        Long expiredTime = System.currentTimeMillis() / 1000 + EXPIRED; // 2个小时失效
        // 初始化秘钥信息
        Credentials cred = new Credentials(APP_ID, SECRET_ID, SECRET_KEY);

        SysConfig configImg = refresh(BUCKET, cred, expiredTime, CONFIG_CODE);
        SysConfig configFile = refresh(BUCKET_FILE, cred, expiredTime, CONFIG_CODE_FILE);

        if (BUCKET_TYPE_IMG.equals(type)) {
            return configImg;
        } else if (BUCKET_TYPE_FILE.equals(type)) {
            return configFile;
        }

        return null;
    }

    /**
     * 刷新多次签名
     *
     * @param bucket
     * @param cred
     * @param expiredTime
     * @param configCode
     * @return
     */
    private SysConfig refresh(String bucket, Credentials cred, Long expiredTime, String configCode) {
        String sign = "";
        try {
            sign = Sign.getPeriodEffectiveSign(bucket, "", cred, expiredTime);
        } catch (AbstractCosException e) {
            e.printStackTrace();
        }
        SysConfig config = sysConfigService.findByCode(configCode);
        if (null == config) {
            config = new SysConfig();
            config.setCode(configCode);
            config.setName("腾讯云对象存储签名");
            config.setType(1);
        }
        config.setValue(sign);
        if (Strings.isNullOrEmpty(config.getId())) {
            sysConfigService.insert(config);
        } else {
            config.setUpdateDate(new Date());
            sysConfigService.update(config);
        }
        return config;
    }

    /**
     * 用于提供配置信息 key和secret不允许暴露出去
     *
     * @param type: bucket类型 0: 图片, 1: 文件
     * @return
     */
    public Map<String, Object> getConfig(Integer type) {
        HashMap<String, Object> config = Maps.newHashMap();
        config.put("app_id", APP_ID);
        if (null == type) {
            type = 0;
        }
        if (Integer.valueOf(0).equals(type)) {
            config.put("bucket", BUCKET);
        } else if (Integer.valueOf(1).equals(type)) {
            config.put("bucket", BUCKET_FILE);
        }
        config.put("region", REGION);
        return config;
    }


    /**
     * 上传远程图片
     *
     * @param url
     * @param memberId 上传用户 用于拼接cospath
     * @param fileName 文件名 可不填 默认为当前时间戳
     * @param cInput   压缩参数 不传则不压缩
     * @return
     */
    public Map uploadImgRemote(String url, String memberId, String fileName, CompressInput cInput) {
        String suffix = url.substring(url.lastIndexOf("/") + 1).indexOf(".") == -1 ? ".jpg" : url.substring(url.lastIndexOf("."));
        if (Strings.isNullOrEmpty(fileName)) {
            fileName = System.currentTimeMillis() + suffix;
        } else {
            if (fileName.indexOf(".") == -1) {
                fileName += suffix;
            }
        }
        byte[] bytes = null;
        if (null == cInput || Strings.isNullOrEmpty(cInput.getStrUrl())) {
            bytes = ImgUtil.getImageFromNetByUrl(url);
        } else {
            try {
                CompressResult compress = ImgCompressUtil.compress(cInput);
                bytes = compress.getBytes();
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("图片压缩异常：图片路径——" + cInput.getStrUrl(), e);
            }
        }
        UploadFileRequest overWriteFileRequest =
                new UploadFileRequest(BUCKET, "/" + memberId + "/image/" + fileName, bytes);
        // 如果COS上已有文件, 则进行覆盖(默认不覆盖)
        overWriteFileRequest.setInsertOnly(InsertOnly.OVER_WRITE);
        String overWriteFileRet = cosClient.uploadFile(overWriteFileRequest);
        Map map = JSONObject.parseObject(overWriteFileRet, Map.class);
        if ((Integer) map.get("code") != 0) {
            logger.error("图片上传失败：结果——" + overWriteFileRet, map);
            return null;
        }
        return map;
    }

    /**
     * 删除图片
     *
     * @param cosPath
     * @return
     */
    public Map delImgByCosPath(String cosPath) {
        String headPath = "image.myqcloud.com";
        if (cosPath.indexOf(headPath) == -1) {
            logger.info("delImgByCosPath 异常：cosPath-" + cosPath);
            return new HashMap();
        }
        cosPath = cosPath.substring(cosPath.indexOf(headPath) + headPath.length());
        DelFileRequest delFileRequest = new DelFileRequest(BUCKET, cosPath);
        String delFileRet = cosClient.delFile(delFileRequest);
        Map map = JSONObject.parseObject(delFileRet, Map.class);
        return map;
    }

    /**
     * 获取图片的cdn路径和图片的cos路径
     *
     * @param map
     * @return
     */
    public Map<String, Object> handelImgUrl(Map map) {
        String resource_path = (String) map.get("resource_path");
        String iAppid = resource_path.split("/")[1];
        String iBucket = resource_path.split("/")[2];
        String cdnUrl = "http://" + iBucket + "-" + iAppid + ".image.myqcloud.com" + resource_path.replace("/" + iAppid + "/" + iBucket, "");
        String sourceUrl = "http://" + iBucket + "-" + iAppid + ".cos" + REGION + ".myqcloud.com" + resource_path.replace("/" + iAppid + "/" + iBucket, "");
        HashMap<String, Object> ret = Maps.newHashMap();
        ret.put("cdnUrl", cdnUrl);// 图片的cdn路径
        ret.put("sourceUrl", sourceUrl);// 图片的cos路径
        return ret;
    }

    /**
     * 上传文件
     *
     * @param cosFilePath 文件目录
     * @return
     * @throws Exception
     */
    public UploadResult uploadFile(String cosFilePath, InsertOnly insertOnly, CompressInput cInput) throws Exception {
        byte[] contentBuffer = null;
        try {
            CompressResult compress = ImgCompressUtil.compress(cInput);
            contentBuffer = compress.getBytes();
        } catch (Exception e) {
            logger.error("图片压缩异常：图片路径——" + cosFilePath, e);
            throw e;
        }
        return getUploadResult(contentBuffer, cosFilePath, insertOnly);
    }

    private UploadResult getUploadResult(byte[] contentBuffer, String cosFilePath, InsertOnly insertOnly) throws Exception {
        UploadFileRequest overWriteFileRequest = new UploadFileRequest(BUCKET, cosFilePath, contentBuffer);
        overWriteFileRequest.setInsertOnly(insertOnly);
        String overWriteFileRet = cosClient.uploadFile(overWriteFileRequest);
        logger.info("{}\n文件上传结果：{}", cosFilePath, overWriteFileRet);
        UploadResult uploadResult = JSONObject.parseObject(overWriteFileRet, UploadResult.class);
        if (SUCCESS_CODE.equals(uploadResult.getCode()) && SUCCESS_MSG.equals(uploadResult.getMessage())) {
            String dataStr = uploadResult.getData();
            return JSONObject.parseObject(dataStr, UploadResult.class);
        } else {
            throw new Exception(overWriteFileRet);
        }
    }

    /**
     * 上传文件
     *
     * @param inputStream 文件流
     * @param cosFilePath 文件目录
     * @return
     * @throws Exception
     */
    public UploadResult uploadFile(InputStream inputStream, String cosFilePath, InsertOnly insertOnly) throws Exception {
        byte[] contentBuffer = CommonFileUtils.getFileContent(inputStream, 0L, inputStream.available())
                .getBytes(Charset.forName(("ISO-8859-1")));
        return getUploadResult(contentBuffer, cosFilePath, insertOnly);
    }

    /**
     * 获取目录属性
     *
     * @param cosFolderPath 目录
     * @return
     */
    public SearchResult statFolder(String cosFolderPath) {
        StatFolderRequest statFolderRequest = new StatFolderRequest(BUCKET, cosFolderPath);
        String statFolderRet = cosClient.statFolder(statFolderRequest);
        return JSONObject.parseObject(statFolderRet, SearchResult.class);
    }

    /**
     * 生成目录
     *
     * @param cosFolderPath 目录
     * @return
     */
    public boolean createFolder(String cosFolderPath) {
        CreateFolderRequest createFolderRequest =
                new CreateFolderRequest(BUCKET, cosFolderPath);
        String createFolderRet = cosClient.createFolder(createFolderRequest);
        Map<String, Object> map = JSONObject.parseObject(createFolderRet, Map.class);
        Integer code = (Integer) map.get("code");
        String message = (String) map.get("message");
        return SUCCESS_CODE.equals(code) && SUCCESS_MSG.equals(message) ? true : false;
    }

    /**
     * 删除文件
     *
     * @param cosFilePath 文件路径
     * @return
     */
    public boolean deleteFile(String cosFilePath) {
        DelFileRequest delFileRequest = new DelFileRequest(BUCKET, cosFilePath);
        String delFileRet = cosClient.delFile(delFileRequest);
        Map<String, Object> map = JSONObject.parseObject(delFileRet, Map.class);
        Integer code = (Integer) map.get("code");
        String message = (String) map.get("message");
        return SUCCESS_CODE.equals(code) && SUCCESS_MSG.equals(message) ? true : false;
    }

    public String getPreviewPicUrl(String accessUrl) {
        if (StringUtils.isNotEmpty(accessUrl)) {
            accessUrl = accessUrl.replace("file", "image");
        }
        return accessUrl;
    }

    public String getPreviewPictureUrl(String resourcePath, String extension) {
        if (StringUtils.isNotEmpty(resourcePath)) {
            String url = "http://" + this.BUCKET + "-" + this.APP_ID + "." + QCLOUD_DOMAIN + "/" + resourcePath + "." + extension;
            return url;
        }
        return "";
    }

    public String getPreviewPictureUrl2(String resourcePath, String extension) {
        if (StringUtils.isNotEmpty(resourcePath)) {
            String url = "http://" + this.BUCKET + "-" + this.APP_ID + ".cossh.myqcloud.com/" + resourcePath + "." + extension;
            return url;
        }
        return "";
    }

    public String getPreviewPictureUrl(String resourcePath) {
        if (StringUtils.isNotEmpty(resourcePath)) {
            String url = "http://" + this.BUCKET + "-" + this.APP_ID + "." + QCLOUD_DOMAIN + "/" + resourcePath + ".jpg";
            return url;
        }
        return "";
    }

    /**
     * 获取文件属性
     *
     * @param cosFilePath 文件路径
     * @return
     */
    public String statFile(String cosFilePath) {
        StatFileRequest statFileRequest = new StatFileRequest(BUCKET, cosFilePath);
        String statFileRet = cosClient.statFile(statFileRequest);
        Map<String, Object> map = JSONObject.parseObject(statFileRet, Map.class);
        logger.info("statFile 结果：{}", JSONObject.toJSONString(map));
        Integer code = (Integer) map.get("code");
        String message = (String) map.get("message");
        if (SUCCESS_CODE.equals(code) && SUCCESS_MSG.equals(message)) {
            Map<String, Object> fileMap = JSONObject.parseObject(map.get("data").toString(), Map.class);
            return fileMap.get("access_url").toString();
        }
        return null;
    }

    /**
     * 获取删除二维码路径
     *
     * @param folderName 目录名称
     * @param fileName   文件名称
     * @return
     */
    public static String getDeleteQrCodePath(String folderName, String fileName) {
        return "/" + folderName + fileName + ".jpg";
    }

    /**
     * @return
     * @Author yifeng
     * @Description 获取小程序对象操作临时秘钥
     * @Date 13:54 2019/5/14
     **/
    public Map<String, Object> getCosStsCredentials() throws Exception {
        TreeMap<String, Object> config = new TreeMap<String, Object>();

        // 云 API 密钥 secretId
        config.put("secretId", SECRET_ID);
        // 云 API 密钥 secretKey
        config.put("secretKey", SECRET_KEY);

        // 临时密钥有效时长，单位是秒
        config.put("durationSeconds", 1800);

        // 换成你的 bucket
        config.put("bucket", BUCKET + "-" + APP_ID);
        // 换成 bucket 所在地区
        config.put("region", "ap-" + REGION_FULL);

        // 设置可操作的资源路径前缀，根据实际情况进行设置
        // 如授予可操作所有的资源：则为 *；
        // 如授予操作某个路径a下的所有资源，则为 a/*；
        // 如授予只能操作某个特定路径的文件 a/test.jpg， 则为 a/test.jpg
        config.put("allowPrefix", "*");

        // 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
        String[] allowActions = new String[]{
                // 简单上传
                "name/cos:PutObject",
                // 表单上传
                "name/cos:PostObject",
                // 删除对象
                "name/cos:DeleteObject"
        };
        config.put("allowActions", allowActions);
        // 请求临时密钥信息
        org.json.JSONObject credential = CosStsClient.getCredential(config);
        String jsonStr = credential.toString(4);
        return JSONObject.parseObject(jsonStr, Map.class);
    }

    /**
     * 获取图片大小
     *
     * @param imgUrl 远程图片链接
     * @return 图片传输大小
     * @author yifeng
     * @date 16:32 2019/9/24
     **/
    public int getImageLength(String imgUrl) {
        int length = 0;
        URL url;
        try {
            url = new URL(imgUrl);
            HttpURLConnection urlcon = (HttpURLConnection) url.openConnection();//打开连接
            //根据响应获取文件大小
            length = urlcon.getContentLength();
            urlcon.disconnect();//关闭连接
        } catch (Exception e) {
            e.printStackTrace();
        }
        return length;
    }

    /**
     * @Description:通过图片链接获取图片宽高
     * @Param:imgUrl
     * @return:mmp
     * @Author: Hua Qunhao
     * @Date: 2019/9/24
     */
    public Map<String, Integer> getImageWidthAndHeight(String imgUrl) {
        Map<String, Integer> mmp = Maps.newHashMap();
        mmp.put("width", 0);
        mmp.put("height", 0);
        if (StringUtils.isEmpty(imgUrl)) {
            return mmp;
        }
        try {
            InputStream murl = new URL(imgUrl).openStream();
            BufferedImage sourceImg = ImageIO.read(murl);
            mmp.put("width", sourceImg.getWidth());// 源图宽度
            mmp.put("height", sourceImg.getHeight()); // 源图高度
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mmp;
    }
}
